using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public enum MarbleGameState {playing, won,lost };

public class MarbleGameManager : MonoBehaviour
{
    public static MarbleGameManager Instance;

    private int totalGems;
    private int foundGems;
    private MarbleGameState gameState;
	private bool alreadyLoaded;
	public GameObject[] levelBasis;
    public int collisions;
    private GameObject[] objects;
    private int[] parentIdx;
    private int[] parentDir;
    private float[] parentDist;
    private float[] parentDist2;
    private float[] initialHeight;
    private Quaternion[] rotations;
    private Vector3[] positions;
    private bool[] hasModifications;


    void Awake()
    {
        Instance = this; 
        foundGems = 0;
		collisions = 0;
        gameState = MarbleGameState.playing;
        totalGems = GameObject.FindGameObjectsWithTag("Pickup").Length;
		levelBasis = GameObject.FindGameObjectsWithTag("Pickup");
        Time.timeScale = 1.0f;
		alreadyLoaded = false;
		
		objects = FindObjectsOfType(typeof(GameObject)) as GameObject[];
		float h = 0.0f;
		//float R = 20.0f;
		

        //foreach (GameObject o in objects)
        //{
		
        //    if (o.name == "Light")
        //    {
        //        continue;
        //    }
        //    //o.transform.position += new Vector3(0.0f, h, 0.0f);
        //    h += 4.0f/R;
			
        //    Vector3 normal = new Vector3(Mathf.Sin(h),Mathf.Cos(h),0.0f);
        //    Vector3 direction = normal;
        //    direction.x = normal.y;
        //    direction.y = -normal.x;
        //    //o.transform.position = new Vector3(normal.x,normal.y, normal.z) * R;
        //    //o.transform.rotation =Quaternion.FromToRotation(new Vector3(1,0,0), direction);
        //}
		
		int sz = objects.Length;
		int rootIdx = -1;
		
		parentIdx = new int[sz];
		parentDir = new int[sz];
        parentDist = new float[sz];
        parentDist2 = new float[sz];
        initialHeight = new float[sz];
        rotations = new Quaternion[sz];
        positions = new Vector3[sz];
        hasModifications = new bool[sz];

		Transform [] oldTransform = new Transform[sz];

        bool recalc = false;

        if (false)//!recalc)
        {
            TextAsset asset = Resources.Load("temp") as TextAsset;
            Stream s = new MemoryStream(asset.bytes);
            BinaryReader br = new BinaryReader(s);

            int count = br.ReadInt32();

            Debug.Log("count = " + count);

            for (int i = 0; i < count; ++i)
            {
                //Debug.Log("iter = " + i);
                parentIdx[i] = br.ReadInt32();
                parentDir[i] = br.ReadInt32();
                parentDist[i] = br.ReadSingle();
                parentDist2[i] = br.ReadSingle();
                initialHeight[i] = br.ReadSingle();

                positions[i].x = br.ReadSingle();
                positions[i].y = br.ReadSingle();
                positions[i].z = br.ReadSingle();

                rotations[i].w = br.ReadSingle();
                rotations[i].x = br.ReadSingle();
                rotations[i].y = br.ReadSingle();
                rotations[i].z = br.ReadSingle();

                hasModifications[i] = br.ReadBoolean();

                objects[i].transform.position = positions[i];
                objects[i].transform.rotation = rotations[i];

                if (hasModifications[i])
                {
                    Mesh mesh = objects[i].GetComponent<MeshFilter>().mesh;

                    Vector3[] vertices = mesh.vertices;
                    Vector3[] normals = mesh.normals;
    
                    const float koef = 0.04f;

                    for (int vertIdx = 0; vertIdx < vertices.Length; ++vertIdx)
                    {
                        float mf = Vector3.Dot(normals[vertIdx], Vector3.up);
                        normals[vertIdx].x += vertices[vertIdx].x * (mf * koef);
                        normals[vertIdx].z += vertices[vertIdx].z * (mf * koef);
                        vertices[vertIdx].x *= 1.0f + (vertices[vertIdx].y + initialHeight[i]) * koef;
                        vertices[vertIdx].z *= 1.0f + (vertices[vertIdx].y + initialHeight[i]) * koef;
                    }

                    mesh.vertices = vertices;
                    mesh.normals = normals;
                }

                if (objects[i].name == "Center")
                {
                    objects[i].particleEmitter.minSize = 50.0f;
                    objects[i].particleEmitter.maxSize = 50.0f;
                    objects[i].particleEmitter.ClearParticles();
                    objects[i].particleEmitter.Emit();
                }
                else if (objects[i].name == "GameoverTrigger")
                {
                    SphereCollider col = objects[i].collider as SphereCollider;
                    col.radius = 20.0f;
                }

                //Debug.Log("check = " + positions[i].y);
            }

        }



       

        if (recalc)
        {



            for (int i = 0; i < sz; ++i)
            {
                parentIdx[i] = -1;
                parentDir[i] = -1;
                parentDist[i] = -1.0f;
                parentDist2[i] = -1.0f;
                initialHeight[i] = objects[i].transform.position.y;
                hasModifications[i] = false;

                if (objects[i].name == "Start")
                {
                    rootIdx = i;
                }
                else if (objects[i].name == "Center")
                {
                    objects[i].particleEmitter.minSize =  50.0f;
                    objects[i].particleEmitter.maxSize = 50.0f;
                    objects[i].particleEmitter.ClearParticles();
                    objects[i].particleEmitter.Emit();
                }
				else if (objects[i].name == "Atmos")
                {
                    objects[i].particleEmitter.minSize =  90.0f;
                    objects[i].particleEmitter.maxSize = 90.0f;
                    objects[i].particleEmitter.ClearParticles();
                    objects[i].particleEmitter.Emit();
					objects[i].renderer.materials[0].renderQueue = 2501;

					//objects[i]. materials[0].renderQueue = 1;
                }
                else if (objects[i].name == "GameoverTrigger")
                {
                    SphereCollider col = objects[i].collider as SphereCollider;
                    col.radius = 20.0f;
                }
                else if (objects[i].name == "raznoves")
                {
                    
                }
                else
                {
                    Renderer[] rs = objects[i].GetComponents<Renderer>();
                    if (rs.Length > 0)
                    {
                        for (int m = 0; m < rs[0].materials.Length; ++m)
                        {

                            rs[0].materials[m].renderQueue = 2502;
                        }
                    }

                    HingeJoint[] joints = objects[i].GetComponents<HingeJoint>();

                    if (joints.Length > 0)
                    {

                       // objects[i].Remov

                        
                        Destroy(joints[0]);

                    }
                }
            }

            int currentNode = rootIdx;
            List<int> openNodes = new List<int>();
            List<int> closedNodes = new List<int>();

            for (int i = 0; i < objects.Length; ++i)
            {
                if (i != rootIdx)
                {
                    closedNodes.Add(i);
                }
            }

            while (closedNodes.Count + openNodes.Count > 0)
            {
                List<int> toRemove = new List<int>();

                for (int k = 0; k < closedNodes.Count; ++k)
                {
                    int node = closedNodes[k];
                    if (node != currentNode && parentIdx[node] == -1)
                    {
                        Vector3 dist = objects[node].transform.position - objects[currentNode].transform.position;
                        if (!objects[node].collider || !objects[currentNode].collider)
                        {
                            continue;
                        }
                        float minDist = objects[node].collider.bounds.extents.x + objects[currentNode].collider.bounds.extents.x;
                        const float eps = 0.1f;
                        if (Mathf.Abs(dist.magnitude - minDist) < eps)
                        {
                            parentDist[node] = objects[node].collider.bounds.extents.x * 2;// minDist;
                            parentDist2[node] = minDist;

                            parentIdx[node] = currentNode;
                            if (dist.x > minDist - eps)
                            {
                                parentDir[node] = 1;
                            }
                            else if (dist.x < -minDist + eps)
                            {
                                parentDir[node] = 3;
                            }
                            else if (dist.z > minDist - eps)
                            {
                                parentDir[node] = 0;
                            }
                            else if (dist.z < -minDist + eps)
                            {
                                parentDir[node] = 2;
                            }
                            openNodes.Add(node);
                            toRemove.Add(node);
                            //Debug.Log("wave " + objects[node].name);

                        }

                    }

                }

                for (int k = 0; k < toRemove.Count; ++k)
                {
                    closedNodes.Remove(toRemove[k]);
                }

                if (openNodes.Count == 0)
                {
                    break;
                }
                else
                {
                    currentNode = openNodes[openNodes.Count - 1];
                    openNodes.Remove(currentNode);
                    //Debug.Log("x" + currentNode);
                }

            }

            for (int i = 0; i < objects.Length; ++i)
            {
                if (i != rootIdx && parentIdx[i] == -1)
                {
                    Vector3 myPos = objects[i].transform.position;
                    float minDist = 999.0f;
                    int minIdx = -1;
                    for (int k = 0; k < objects.Length; ++k)
                    {
                        if (k == i) continue;
                        float dist = (objects[k].transform.position - myPos).magnitude;
                        if (dist < minDist && parentDir[k] != -1)
                        {
                            minIdx = k;
                            minDist = dist;
                        }


                    }
                    parentIdx[i] = minIdx;
                    //Debug.Log("nua cho " + minIdx);

                }
            }
            Vector3 p1 = new Vector3(0, 25, 0);
            getInPosition(rootIdx, p1, new Vector3(-1, 0, 0), Quaternion.identity, true, 2, 0.5f, 4.0f, 4.0f);

            //Debug.Log("pishu = " + sz);
            //MemoryStream stream = new MemoryStream();
            //BinaryWriter buf = new BinaryWriter(stream);
            //buf.Write(sz);
 
            //for (int i = 0; i < sz; ++i)
            //{
            //    buf.Write(parentIdx[i]);
            //    buf.Write(parentDir[i]);
            //    buf.Write(parentDist[i]);
            //    buf.Write(parentDist2[i]);
            //    buf.Write(initialHeight[i]);

            //    buf.Write(positions[i].x);
            //    buf.Write(positions[i].y);
            //    buf.Write(positions[i].z);

            //    buf.Write(rotations[i].w);
            //    buf.Write(rotations[i].x);
            //    buf.Write(rotations[i].y);
            //    buf.Write(rotations[i].z);

            //    buf.Write(hasModifications[i]);
            //}

            //byte[] arr = stream.ToArray();

            //FileStream fs = new FileStream("C:/another-marble/Assets/Resources/temp.bytes", FileMode.Create);
            //fs.Write(arr, 0, arr.Length);
            //fs.Close();


        }
        else
        {

        }
    }

    void getInPosition(int rootIdx, Vector3 pb, Vector3 axis, Quaternion parent, bool root, int prevOffset, float deep, float len, float len2)
    {
        float R = 25.0f;// +initialHeight[rootIdx];
        //float len = 4.0f;



        float alpha = 2 * Mathf.Acos(Mathf.Sqrt(1 - len * len / (4 * R * R))) * Mathf.Rad2Deg;
        float alphas = 2 * Mathf.Acos(Mathf.Sqrt(1 - len2 * len2 / (4 * R * R))) * Mathf.Rad2Deg;



        Quaternion deltaRot = Quaternion.AngleAxis(alpha, axis);
        Vector3 pf = Quaternion.AngleAxis(alpha, axis) * pb;
        Vector3 center = (pb + pf) * 0.5f;

        Vector3 orto = Vector3.Cross(center, pf - pb);
        orto.Normalize();

        Vector3 pr = center + orto * len * 0.5f;
        Vector3 pl = center - orto * len * 0.5f;

        Vector3 initialParentPos = objects[rootIdx].transform.position;
        Vector3 deltaPos = center - objects[rootIdx].transform.position;
        objects[rootIdx].transform.position = center;

        Quaternion deltaRotFull = Quaternion.AngleAxis(alphas*deep, axis);

        const float koef = 0.04f;

        Mesh mesh = objects[rootIdx].GetComponent<MeshFilter>().mesh;
        hasModifications[rootIdx] = true;
        Vector3[] vertices = mesh.vertices;
        Vector3[] normals = mesh.normals;
        //ector3 centerDir = new Vector3(0, 1, 0);
        //centerDir.Normalize();
        StringReader r;
        for (int i = 0; i < vertices.Length; ++i)
        {
            float mf = Vector3.Dot(normals[i], Vector3.up);
            normals[i].x += vertices[i].x * (mf * koef);
            normals[i].z += vertices[i].z * (mf * koef);
            vertices[i].x *= 1.0f + (vertices[i].y +initialHeight[rootIdx])* koef;
            vertices[i].z *= 1.0f + (vertices[i].y + initialHeight[rootIdx] )* koef;


        }

        mesh.vertices = vertices;
        mesh.normals = normals;

        Vector3 upDir = objects[rootIdx].transform.position;
        upDir.Normalize();

        deltaPos += upDir * initialHeight[rootIdx];
        objects[rootIdx].transform.position += upDir * initialHeight[rootIdx];
        //Mesh mesh2 = objects[rootIdx].GetComponent<MeshCollider>().mesh;

        //Vector3[] vertices2 = mesh2.vertices;

        //for (int i = 0; i < vertices2.Length; ++i)
        //{
        //    vertices2[i].y += initialHeight[rootIdx];
        //    vertices2[i].x *= 1.0f + vertices2[i].y * koef;
        //    vertices2[i].z *= 1.0f + vertices2[i].y * koef;

        //}

        //mesh2.vertices = vertices2;

        objects[rootIdx].transform.rotation = deltaRotFull * parent * objects[rootIdx].transform.rotation;

        positions[rootIdx] = objects[rootIdx].transform.position;
        rotations[rootIdx] = objects[rootIdx].transform.rotation;
  

        for (int i = 0; i < parentIdx.Length; ++i)
        {
            if (parentIdx[i] == rootIdx)
            {

                if (parentDir[i] != -1)
                {
                    Quaternion sumRot = deltaRotFull * parent;

                    Vector3 newAxis = Vector3.Cross(center, axis);
                    newAxis.Normalize();

                    if (prevOffset == parentDir[i])
                    {
                        getInPosition(i, pf, axis, sumRot, false, parentDir[i], 1, parentDist[i], parentDist2[i]);
                    }
                    else if ((4 + parentDir[i] - prevOffset) % 4 == 1)
                    {
                        getInPosition(i, pr, newAxis, sumRot, false, parentDir[i], 1, parentDist[i], parentDist2[i]);
                    }
                    else
                    {
                        getInPosition(i, pl, -newAxis, sumRot, false, parentDir[i], 1, parentDist[i], parentDist2[i]);
                    }

                }
                else
                {
                    if (objects[i].name == "raznoves")
                    {

                        Vector3 myRelativePos = objects[i].transform.position - initialParentPos;

                        objects[i].transform.position = initialParentPos + deltaPos + objects[rootIdx].transform.rotation * myRelativePos;
                        objects[i].transform.rotation = objects[rootIdx].transform.rotation * objects[i].transform.rotation;

                        positions[i] = objects[i].transform.position;
                        rotations[i] = objects[i].transform.rotation;
                        HingeJoint joint = objects[i].AddComponent<HingeJoint>();
                        joint.anchor = new Vector3(0, 0, 0);
                        joint.axis = new Vector3(0, 0, 1);
                        JointLimits limits = new JointLimits();
                        limits.min = 0;
                        limits.max = 37;
                        joint.limits = limits;

                    }
                    else if (objects[i].name != "GameoverTrigger" && objects[i].name != "Center")
                    //if (objects[i].name == "Marble")

                    {


                        Vector3 myRelativePos = objects[i].transform.position - initialParentPos;

                        objects[i].transform.position = initialParentPos + deltaPos + objects[rootIdx].transform.rotation * myRelativePos;
                        objects[i].transform.rotation = objects[rootIdx].transform.rotation * objects[i].transform.rotation;

                        positions[i] = objects[i].transform.position;
                        rotations[i] = objects[i].transform.rotation;

                        Carousel[] car = objects[i].GetComponents<Carousel>();

                        if (car.Length > 0)
                        {
                            car[0].init();
                        }
                    }
                }
            }

        }
    }

    void OnGUI()
    {
	    GUILayout.Label(" Found gems: "+foundGems+"/"+totalGems );
		
		GUILayout.Label(" Collisions : "+collisions);

        if (gameState == MarbleGameState.lost)
        {
            GUILayout.Label("You Lost!");
            if(GUILayout.Button("Try again") )
            {
                Application.LoadLevel(Application.loadedLevel);
            }
        }
        else if (gameState == MarbleGameState.won)
        {
            GUILayout.Label("You won!");
            if(GUILayout.Button("Play again") )
            {
                Application.LoadLevel(Application.loadedLevel);
            }
        }
	}
	
	public void NextLevel()
	{
		if (Application.loadedLevelName != "level2" && !alreadyLoaded)
		{
			Vector3 from;
			
			GameObject[] hs = FindObjectsOfType(typeof(GameObject)) as GameObject[];
            foreach (GameObject h in hs)
            {
			    if (h.name == "Portal")
			    {
				    from = h.transform.position;
			    }
			    if (h.name != "Turn" && h.name != "Straight")
			    {
                    Destroy(h);
			    }
            }
		
		    Vector3 pos = new Vector3(0,0,0);
			
            GameObject[] hs2 = FindObjectsOfType(typeof(GameObject)) as GameObject[];
            foreach (GameObject h in hs2)
            {
			    if (h.name == "Start")
			    {
    				pos = h.transform.position;
	    		}
            }
		
	        foreach (GameObject h in hs2)
            {
			    h.transform.position+= new Vector3(1.0f,1.0f,1.0f);
            }
		
			Application.LoadLevelAdditive("level2");
			alreadyLoaded = true;
		}
	}

    public void FoundGem()
    {
        foundGems++;

        if (foundGems >= totalGems)
        {
            WonGame();
        }
    }
	
	public void onAddCollision()
	{
		collisions++;
		
	}
	
	public void onRemoveCollision()
	{
		collisions--;
	}

    public void WonGame()
    {
        Time.timeScale = 0.0f; //Pause game
        gameState = MarbleGameState.won;
    }

    public void SetGameOver()
    {
        Time.timeScale = 0.0f; //Pause game
        gameState = MarbleGameState.lost;
    }
}
